package com.jie.server.core.runnable;

import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import com.jie.common.ssl.constant.SslConstant;
import com.jie.common.ssl.util.AesUtil;
import com.jie.common.ssl.util.DigestUtil;
import com.jie.common.util.BytesUtil;
import com.jie.common.util.InputStreamUtil;
import com.jie.common.util.OutputStreamUtil;
import com.jie.common.util.RandomUtil;
import com.jie.server.core.properties.ServerProperties;
import com.jie.server.core.selector.ServerSelector;
import com.jie.server.core.entity.ChannelAttachment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

/**
 * 用于处理accept事件的Runnable对象
 * @author XieZhiJie
 * @date 2022/04/22 12:06
 */
public class AcceptHandlerRunnable implements Runnable {

    private final Logger logger = LoggerFactory.getLogger(AcceptHandlerRunnable.class);


    private final SelectionKey key;

    private final SocketChannel socketChannel;

    public AcceptHandlerRunnable(SelectionKey key, SocketChannel socketChannel) {
        this.key = key;
        this.socketChannel = socketChannel;
    }

    @Override
    public void run() {
        try {
            handleAccept();
        } catch (Exception e) {
            logger.error(e.getMessage());
            try {
                this.socketChannel.close();
            } catch (IOException ioException) {
                logger.error(ioException.getMessage());
            }
        }
    }

    /**
     * 处理监听的方法
     * @throws Exception 抛出异常并终止channel
     */
    public void handleAccept() throws Exception {
        ChannelAttachment channelAttachment = new ChannelAttachment(ServerProperties.BUFFER_SIZE);

        // SSL
        channelAttachment.setAes(serverSslHandShake(this.socketChannel, SslConstant.OPEN_SSL));

        this.socketChannel.configureBlocking(false);
        // 因为有锁, 不需要担心register的时候的并发问题
        this.socketChannel.register(this.key.selector(), SelectionKey.OP_READ, channelAttachment);
    }

    /**
     * 服务端ssl的握手
     * @param socketChannel socketChannel暂时不用于注册, 等握手成功再注册
     * @param flag          是否开启ssl握手
     * @return              返回一个使用会话密钥生成的AES对象
     */
    public AES serverSslHandShake(SocketChannel socketChannel, boolean flag) throws Exception {
        if (flag) {
            return serverSslHandShake(socketChannel);
        }
        return null;
    }

    /**
     * 服务端ssl的握手
     * @param socketChannel socketChannel暂时不用于注册, 等握手成功再注册
     * @return              返回一个使用会话密钥生成的AES对象
     */
    public AES serverSslHandShake(SocketChannel socketChannel) throws Exception {
        InputStream inputStream = socketChannel.socket().getInputStream();
        OutputStream outputStream = socketChannel.socket().getOutputStream();
        try {

            // 每个发送的字节数组不需要理会报文头

            // 1.接收客户端的第一次握手, 得到随机数x
            byte[] firstHandShakeBytes = InputStreamUtil.readMessage(inputStream);

            // 2.生成随机数y并与证书一起发送(发送第二次握手内容)
            byte[] randomServerBytes = RandomUtil.getRandomBytes();
            byte[] secondHandShakeBytes = BytesUtil.packageTwoBytesWithLengthBytes(randomServerBytes, ServerSelector.SERVER_SSL_MESSAGE.certificateByte);

            OutputStreamUtil.writeMessage(outputStream, secondHandShakeBytes);

            // 3.1.接收到第三次握手的内容, 用服务器私钥解密得到随机数pre与前两次握手的摘要
            byte[] thirdHandShakeBytes = InputStreamUtil.readMessage(inputStream);
            byte[] decryptThirdHandShake = new RSA(ServerSelector.SERVER_SSL_MESSAGE.privateKeyBytes, ServerSelector.SERVER_SSL_MESSAGE.publicKeyBytes).decrypt(thirdHandShakeBytes, KeyType.PrivateKey);
            byte[][] array = BytesUtil.splitBytesToTwoBytes(decryptThirdHandShake);
            byte[] randomPreBytes = array[0];
            byte[] thirdHandShakeDigestBytes = array[1];

            // 3.2.验证前两次握手与客户端第三次握手的摘要, 验证成功则生成会话密钥
            if (!DigestUtil.verifyDigest(thirdHandShakeDigestBytes, firstHandShakeBytes, secondHandShakeBytes)) {
                throw new Exception("第三次握手摘要验证失败!");
            }
            AES aes = new AES(AesUtil.generateSessionKey(firstHandShakeBytes, randomServerBytes, randomPreBytes));

            // 4.用会话密钥加密前三次握手的摘要并发送(第四次握手)
            byte[] fourthHandShakeBytes = BytesUtil.packetBytes(firstHandShakeBytes, secondHandShakeBytes, thirdHandShakeBytes);
            fourthHandShakeBytes = DigestUtil.generateDigest(fourthHandShakeBytes);
            fourthHandShakeBytes = AesUtil.encrypt(fourthHandShakeBytes, aes);
            OutputStreamUtil.writeMessage(outputStream, fourthHandShakeBytes);
            return aes;
        } catch (IOException e) {
            logger.error(e.getMessage());
            throw new IOException();
        }
    }

}
